﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MathNet.Numerics;
using MathNet.Numerics.Distributions;
using System.IO;
using DiffisionWithDecomposition;

namespace DiffusionAnalyticalSolution
{
    class Program
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="communication"></param>
        /// <param name="analytic"></param>
        /// <param name="Ts">Second</param>
        /// <param name="NumberOfSentMolecules"></param>
        /// <param name="maxNumberOfSymbolDurationsToLookBack"></param>
        static void CalculateFracs(Communication communication, Analytic analytic, double Ts, double NumberOfSentMolecules, int maxNumberOfSymbolDurationsToLookBack)
        {
            for (int i = 1; i < communication._bitValues.Length; i++)
            {
                int j;
                if (maxNumberOfSymbolDurationsToLookBack != 0)
                {
                    j = i - maxNumberOfSymbolDurationsToLookBack;

                    if (j < 0)
                    {
                        j = 0;
                    }
                }
                else
                {
                    j = 0;
                }

                double frac = 0;

                for (; j <= i - 1; j++)
                {
                    if (communication._bitValues[j] != 0)
                    {
                        frac += analytic.DifferenceOfMolecules((i - j - 1) * Ts, (i - j) * Ts);                        
                    }
                }

                communication._fracValues[i - 1] = frac;

                if (i % 1000 == 0)
                {
                    Console.WriteLine(i);
                }
            }
        }

        static double CalculatePC0andPC1Gaussian(Communication communication, double tau, double NumberOfSentMolecules, out double pc1)
        {
            var pc0 = 0.0;
            pc1 = 0.0;
            var count0 = 0;
            var count1 = 0;

            for (int i = 0; i < communication._bitValues.Length - 1; i++)
            {
                if (communication._bitValues[i] == 0)
                {
                    var normal = new Normal(NumberOfSentMolecules * communication._fracValues[i], Math.Sqrt(NumberOfSentMolecules * communication._fracValues[i] * (1 - communication._fracValues[i])));

                    count0++;
                    pc0 += normal.CumulativeDistribution(tau);
                }
                else
                {
                    var normal = new Normal(NumberOfSentMolecules * communication._fracValues[i], Math.Sqrt(NumberOfSentMolecules * communication._fracValues[i] * (1 - communication._fracValues[i])));

                    count1++;
                    pc1 += (1 - normal.CumulativeDistribution(tau));
                }
            }

            pc1 = pc1 / count1;

            return pc0 / count0;
        }

        static double CalculatePC0andPC1Poisson(Communication communication, double tau, double NumberOfSentMolecules, out double pc1)
        {
            var pc0 = 0.0;
            pc1 = 0.0;
            var count0 = 0;
            var count1 = 0;

            for (int i = 0; i < communication._bitValues.Length - 1; i++)
            {
                if (communication._bitValues[i] == 0)
                {
                    
                    var poisson = new Poisson(NumberOfSentMolecules * communication._fracValues[i]);

                    count0++;
                    pc0 += poisson.CumulativeDistribution(tau);
                }
                else
	            {
                    var poisson = new Poisson(NumberOfSentMolecules * communication._fracValues[i]);
                    
                    count1++;
                    pc1 += (1 - poisson.CumulativeDistribution(tau));
	            }
            }

            pc1 = pc1 / count1;

            return pc0 / count0;
        }

        static double CalculatePC0andPC1NONOISE(Communication communication, double tau, out double pc1)
        {
            var pc0 = 0.0;
            pc1 = 0.0;
            var count0Correct = 0;
            var count0Total = 0;
            var count1Correct = 0;
            var count1Total = 0;

            for (int i = 0; i < communication._bitValues.Length - 1; i++)
            {
                if (communication._bitValues[i] == 0)
                {
                    if (communication._fracValues[i] < tau)
                    {
                        count0Correct++;                        
                    }

                    count0Total++;
                }
                else
                {
                    if (communication._fracValues[i] >= tau)
                    {
                        count1Correct++;
                    }

                    count1Total++;
                }
            }

            pc1 = count1Correct / count1Total;

            return count0Correct / count0Total;
        }

        static void Main(string[] args)
        {
            RegularAnalyticSimulator();
        }

        static void RegularAnalyticSimulator()
        {
            var distance = 18;
            var symbolDuration = 0.512;
            var duration = 4; //bits
            var numberOfMolecules = 10000;
            var lookbackBits = 0;

            var communication = new Communication(duration);

            communication.InitializeRandom();

            var analytic = new Analytic(10, distance, 79.4);

            CalculateFracs(communication, analytic, symbolDuration, numberOfMolecules, lookbackBits);

            StreamWriter sw = new StreamWriter(String.Format("Logs\\Analitik Gaussian D={0}, N={1}, Dec={2}, T_s={3}, Duration = {4}, Lookback = {5}.txt", distance,
               numberOfMolecules, false, symbolDuration, duration, lookbackBits), false);

            for (int i = 0; i < numberOfMolecules; i++)
            {
                double pc1;
                var pc0 = CalculatePC0andPC1Gaussian(communication, i, numberOfMolecules, out pc1);

                var I = ShannonsMutualInformation.MutualInformation(pc0, pc1);

                sw.WriteLine(i + " " + 1 + " " + 1 + " " + pc1 + " " + pc0 + " " + I);

                Console.WriteLine(i);
            }

            sw.Flush();
            sw.Close();

        }
    }
}
